#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
import sys

sys.path.append('..')
from common_utils import CBBCommonUtils


def get_plugin_info():
    plugin_info = {
        "name": "centos_access_control_017 Check_log_file_permission_setting",
        "plugin_id": "centos_access_control_017",
        "plugin_type": "Access Control",
        "info": "Writable and executable permissions cannot appear in the same group of users and other user permissions.",
        "level": "B",
        "module": "Safety reinforcement",
        "author": "congshz",
        "keyword": "Safety reinforcement",
        "configable": "false"
    }
    return plugin_info


logger = None
cur_user = None
cur_module = None
cur_task = None


def set_plugin_logger(setter, user, module, task, *args, **kwargs):
    global logger, cur_user, cur_module, cur_task
    logger = setter
    cur_user = user
    cur_module = module
    cur_task = task


def check_log_file(ip, sys_user, sys_pwd):
    des_list = []
    error_count = 0
    file_permission_dict = {}
    check_cmd = "LOGDIR=`cat /etc/rsyslog.conf | grep -v \"^[[:space:]]*#\" |sed '/^#/d' |sed '/^$/d' |awk '(($2!~/@/) && ($2!~/*/) && ($2!~/-/)) {print $2}'`;ls -l $LOGDIR 2>/etc/null | grep \"^-\""
    result, output = CBBCommonUtils.cbb_run_cmd(ip, check_cmd, username=sys_user, passwd=sys_pwd)
    if not result:
        for item in output:
            g_o = str(item).split(".", 1)[0][4:10]
            if "w" in g_o or "x" in g_o:
                error_count += 1
                filename = "/" + str(item).split("/", 1)[1].replace("\n", "")
                log_des = "DANGEROUS File Permission, FILENAME: {}".format(filename)
                logger.debug_warning(cur_user, cur_module, cur_task + '_check_log_file', '', log_des)
                des_list.append(log_des)
                file_permission_dict[filename] = item[:10]
    else:
        cat_error = "CMD process ERROR. Error info: {}.".format(result[0].replace("\n", ""))
        logger.debug_error(cur_user, cur_module, cur_task + '_Scan', '', cat_error)
        error_count += 1
        des_list.append(cat_error)
        return des_list, error_count
    return des_list, error_count, file_permission_dict


# 扫描函数
def scan(ip, sys_user, sys_pwd, flag=0):
    des_list = []
    error_count = 0
    # 检查日志服务状态
    cmd = "service rsyslog status | grep -E \"Active|Loaded\""
    result, output = CBBCommonUtils.cbb_run_cmd(ip, cmd, username=sys_user, passwd=sys_pwd)
    # 检错，每次执行命令都会进行检错，并决定返回退出/继续执行
    if len(output) != 0:
        if "running" in output[1]:
            des = "RSYSLOG Service is Active (running)."
            logger.debug_info(cur_user, cur_module, cur_task + '_Scan', '', des)
            des_list.append(des)

            des_log, error_log, file_permission_dict = check_log_file(ip, sys_user, sys_pwd)
            des_list.extend(des_log)
            error_count += error_log

            if error_count != 0:
                des = "DANGEROUS Log file Permissions."
            else:
                des = "Log file permissions are SAFE."
            logger.debug_info(cur_user, cur_module, cur_task + '_Scan', '', des)
            des_list.append(des)
        elif "not-found" in output[0]:
            des = "RSYSLOG Service not installed."
            logger.debug_warning(cur_user, cur_module, cur_task + '_Scan', '', des)
            des_list.append(des)
            return des_list, error_count
        else:
            des = "RSYSLOG Service is Inactive (dead)."
            error_count += 1
            logger.debug_warning(cur_user, cur_module, cur_task + '_Scan', '', des)
            des_list.append(des)
            return des_list, error_count
    elif len(result) != 1:
        des = "RSYSLOG Service not installed."
        logger.debug_warning(cur_user, cur_module, cur_task + '_Scan', '', des)
        des_list.append(des)
        return des_list, error_count
    else:
        error_des = "CMD process ERROR. Unable to query log service status."
        logger.debug_error(cur_user, cur_module, cur_task + '_Scan', '', error_des)
        des_list.append(error_des)
        error_count += 1
        return des_list, error_count
    des_list.append("Scan Complete.")
    logger.debug_info(cur_user, cur_module, cur_task + '_Scan', '', 'Scan Complete.')
    if error_count != 0:
        # 是否加固
        if flag == 1:
            reinforce_des, reinforce_err = reinforce(ip, file_permission_dict, sys_user, sys_pwd)
            des_list.extend(reinforce_des)
            if reinforce_err == 0:
                error_count = 0
    return des_list, error_count


# 加固函数
def reinforce(ip, file_permission_dict, sys_user, sys_pwd):
    des_list = []
    error_count = 0
    # 将需要更改的日志文件原权限记录至备份文件，文件名中的LP为log_permission缩写，为了针对功能点控制备份文件用，避免备份冲突
    cmd = "echo '" + json.dumps(file_permission_dict) + "' > /etc/LP.`date +%Y-%m-%d`"
    result, output = CBBCommonUtils.cbb_run_cmd(ip, cmd, username=sys_user, passwd=sys_pwd)
    if not result:
        # 检查是否备份成功
        check_cmd = "ls /etc | grep LP.`date +%Y-%m-%d`"
        check_result, check_output = CBBCommonUtils.cbb_run_cmd(ip, check_cmd, username=sys_user, passwd=sys_pwd)
        if not check_result and len(check_output) != 0:
            backup_check_des = "Backup SUCCEED."
            logger.debug_info(cur_user, cur_module, cur_task + '_Reinforce', '', backup_check_des)
        else:
            error_count += 1
            backup_check_des = "Backup check FAILED."
            logger.debug_error(cur_user, cur_module, cur_task + '_Reinforce', '', backup_check_des)
            des_list.append(backup_check_des)
            return des_list, error_count
    else:
        error_count += 1
        backup_des = "Backup FAILED."
        logger.debug_error(cur_user, cur_module, cur_task + '_Reinforce', '', backup_des)
        des_list.append(backup_des)
        return des_list, error_count

    for file in file_permission_dict.keys():
        safe_cmd = "chmod 600 " + file
        CBBCommonUtils.cbb_run_cmd(ip, safe_cmd, username=sys_user, passwd=sys_pwd)

    des_log, error_log, file_permission_dict = check_log_file(ip, sys_user, sys_pwd)
    des_list.extend(des_log)
    error_count += error_log

    if error_count != 0:
        error_count += 1
        des = "Reinforce FAILED."
        logger.debug_error(cur_user, cur_module, cur_task + '_Reinforce', '', des)
        des_list.append(des)
        # 加固失败，回滚
        rollback_des, rollback_err = rollback(ip, sys_user, sys_pwd)
        des_list.extend(rollback_des)
        return des_list, error_count

    des_list.append("Reinforce Complete.")
    logger.debug_info(cur_user, cur_module, cur_task + '_Reinforce', '', "Reinforce Complete.")
    return des_list, error_count


def make_chmod(rwx):
    r = 4 if rwx[0] == "r" else 0
    w = 2 if rwx[1] == "w" else 0
    x = 1 if rwx[2] == "x" else 0
    cmd_num = r + w + x
    return str(cmd_num)


# 回滚函数
def rollback(ip, sys_user, sys_pwd):
    des_list = []
    error_count = 0
    # 查找备份文件，此处的查找支持多备份文件的查找（结果会按时间顺序排序），并默认回滚最新一次备份的内容，可以连续多次回滚
    grep_cmd = "ls /etc/ | grep LP.*"
    result, output = CBBCommonUtils.cbb_run_cmd(ip, grep_cmd, username=sys_user, passwd=sys_pwd)
    if not result and len(output) != 0:
        logger.debug_info(cur_user, cur_module, cur_task + '_Rollback', '', "Backup file FOUND.")
        target = output[len(output) - 1].replace("\n", "")
    else:
        error_count = -1
        des_list.append("Backup file NOT FOUND.")
        logger.debug_error(cur_user, cur_module, cur_task + '_Rollback', '', "Backup file not found.")
        return des_list, error_count
    # 回滚，读取备份文件中记录的原文件权限
    cmd = "cat /etc/" + target
    result, output = CBBCommonUtils.cbb_run_cmd(ip, cmd, username=sys_user, passwd=sys_pwd)
    if not result:
        file_permission_dict = json.loads(output[0].replace("\n", ""))
        for filename in file_permission_dict.keys():
            file_permission = file_permission_dict[filename]
            u_num = make_chmod(str(file_permission[1:4]))
            g_num = make_chmod(str(file_permission[4:7]))
            o_num = make_chmod(str(file_permission[7:10]))
            chmod_cmd = "chmod " + u_num + g_num + o_num + " " + filename
            result, output = CBBCommonUtils.cbb_run_cmd(ip, chmod_cmd, username=sys_user, passwd=sys_pwd)
            if result:
                error_count += 1
        if error_count == 0:
            logger.debug_info(cur_user, cur_module, cur_task + '_Rollback', '', "Rollback SUCCEED.")
            del_cmd = "rm -rf /etc/" + target
            result, output = CBBCommonUtils.cbb_run_cmd(ip, del_cmd, username=sys_user, passwd=sys_pwd)
            if not result:
                del_des = "Backup file deleted. Target is {}.".format(target)
                logger.debug_error(cur_user, cur_module, cur_task + '_Rollback', '', del_des)
            else:
                error_count += 1
                del_des = "Backup file delete FAILED."
                logger.debug_error(cur_user, cur_module, cur_task + '_Rollback', '', del_des)
                des_list.append(del_des)
                return des_list, error_count
        else:
            error_count += 1
            des = "Rollback FAILED, please retry."
            logger.debug_error(cur_user, cur_module, cur_task + '_Rollback', '', des)
            des_list.append(des)
            return des_list, error_count
    else:
        error_count += 1
        des = "Check backup file FAILED."
        logger.debug_error(cur_user, cur_module, cur_task + '_Rollback', '', des)
        des_list.append(des)
    return des_list, error_count


def check(ip, *args, **kwargs):
    sys_user = kwargs.get("system_user")
    sys_pwd = kwargs.get("system_pwd")
    comm = kwargs.get("command")
    try:
        des_list = []
        des_start = "centos_access_control_017 Start."
        logger.debug_info(cur_user, cur_module, cur_task + '_Check', '', des_start)
        if comm == 1 or comm == 2:
            des_scan, error_scan = scan(ip, sys_user, sys_pwd, flag=0)
            des_list.extend(des_scan)
            step_error = int(error_scan)
        elif comm == 3:
            des_rollback, error_rollback = rollback(ip, sys_user, sys_pwd)
            des_list.extend(des_rollback)
            step_error = int(error_rollback)
        else:
            return {"code": 3, "count": 0, "des": ['command must be 1/2/3']}
        des_end = "centos_access_control_017 Complete."
        logger.debug_info(cur_user, cur_module, cur_task + '_Check', '', des_end)
        if step_error == 0:
            code = 0
        elif step_error <= -1:
            code = 2
        else:
            code = 1
        return {"code": code, "count": step_error, "des": des_list}
    except Exception as er:
        code = 1
        des = ["ERROR:", str(er)]
        logger.debug_error(cur_user, cur_module, cur_task + '_Check', '', des)
        return {"code": code, "count": 0, "des": des}

# check(ip="100.2.91.150", system_user="root", system_pwd="admin", command=0, flag=0)
